<?php

namespace common\models\db;

use Carbon\Carbon;
use common\components\wtools\tools\Security;
use common\Tools;
use Yii;
use common\components\wtools\tools\ArrayHelper;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;

/**
 * This is the model class for table "{{%shop_users}}".
 *
 * @author
 */
class ShopUsers extends \common\models\db\tables\ShopUsers
{
    const SCENARIO_TEST = 'test';


    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors = ArrayHelper::merge($behaviors, [
            'timestamp' => [
                'class' => TimestampBehavior::class,
//                'createdAtAttribute' => false,
//                'updatedAtAttribute' => false,
            ],
            'blameable' => [
                'class' => BlameableBehavior::class,
                'createdByAttribute' => false,
                'updatedByAttribute' => false,
            ],
        ]);
        return $behaviors;
    }

    public function attributeLabels()
    {
        $attributeLabels = parent::attributeLabels();
        $attributeLabels = ArrayHelper::merge($attributeLabels, []);
        return $attributeLabels;
    }

    public function rules()
    {
        $rules = parent::rules();
        foreach ($rules as $k => $v) {
            if ($v[1] == 'required'){
                $rules[$k][0] = array_diff($rules[$k][0], ['created_at', 'updated_at', 'created_by', 'updated_by']);
            }
        }
        $rules = ArrayHelper::merge($rules, [
//            [[], 'required', 'on' => self::SCENARIO_TEST],
        ]);
        return $rules;
    }

    public function scenarios()
    {
        $scenarios = parent::scenarios();
        $scenarios = ArrayHelper::merge($scenarios, [
            self::SCENARIO_TEST => [],
        ]);
        return $scenarios;
    }


    /**
     * @return false|mixed|ShopUsers[]
     */
    public static function getAllWithCache()
    {
        return Yii::$app->cache->getOrSet("db-ShopUsers-getAllWithCache", function () {
            return static::find()->all();
        }, null);
    }

    /**
     * @param $id
     * @return false|ShopUsers|mixed
     */
    public static function getByIdWithCache($id)
    {
        return Yii::$app->cache->getOrSet("db-ShopUsers-getByIdWithCache/{$id}", function () use ($id) {
            return static::findOne($id);
        }, null);
    }

    /**
     * @param $token
     * @return ShopUsers|null
     */
    public static function findByToken($token)
    {
        return static::findOne(['token' => $token]);
    }

    public function deleteCaches()
    {
        Yii::$app->cache->delete("db-ShopUsers-getAllWithCache");
        Yii::$app->cache->delete("db-ShopUsers-getByIdWithCache/{$this->id}");
    }

    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        $this->deleteCaches();
    }

    public function afterRefresh()
    {
        parent::afterRefresh();
        $this->deleteCaches();
    }

    public function afterDelete()
    {
        parent::afterDelete();
        $this->deleteCaches();
    }


    # relations

    public static function getFilterShow(ShopUsers $shopUser)
    {
        return [
            'id' => $shopUser->id,
            'nickname' => $shopUser->nickname,
//            'avatar' => $shopUser->avatar,
//            'token' => $shopUser->token,
//            'key' => $shopUser->key,
            'gender' => $shopUser->gender,
            'mobile' => $shopUser->mobile,
        ];
    }
    
    # static funs

    public function test()
    {
        $test = self::instance();
        $test->setScenario(self::SCENARIO_TEST);
        $test->save();
        var_dump($test->toArray());
    }

    # funs

    public function generateToken()
    {
        $token = Tools::generateHasDateUniqueString();
        if (static::findOne(['token' => $token])) {
            $token = Tools::generateHasDateUniqueString();
        }
        $this->token = $token;
        $this->key = Tools::generateRandomStr();
    }

    /**
     * 生成jwtToken
     * @return array
     */
    public function generateJwtToken()
    {
        $header = [
            'alg' => 'MD5_THINK_SLAT',
            'typ' => 'JWT',
        ];
        ksort($header);
        $headerStr = Tools::toJson($header);
        $thinkKey = \Yii::$app->params['jwt']['dlApi']['jwtThinkKey'];
        $createdAt = Carbon::now()->timestamp;
        $endAt = $createdAt + \Yii::$app->params['jwt']['dlApi']['authDuration'];
        $encryptedToken = Security::thinkEncrypt($this->token.$createdAt, $thinkKey);
        $encryptedKey = md5($this->key.$endAt);
        $payload = [
            'token' => $encryptedToken,
            'createdAt' => $createdAt,
            'endAt' => $endAt,
        ];
        ksort($payload);
        $payloadStr = Tools::toJson($payload);
        switch ($header['alg']) {
            case 'MD5_THINK_SLAT':
                $signature = md5($headerStr. $payloadStr. $thinkKey);
                $token = base64_encode(Tools::toJson($header)).'.'.base64_encode($payloadStr).'.'.base64_encode($signature);
                break;
            default:
                throw new \Exception('不支持的加密算法');
        }
        return [
            'token' => $token,
            'encryptedMpUserKey' => $encryptedKey,
        ];
    }

    /**
     * 从jwtToken获取用户
     * @param array $jwtTokenArr
     * @return array
     * @throws \Exception
     */
    public static function getJwtToken($jwtTokenArr)
    {
        $thinkKey = \Yii::$app->params['jwt']['dlApi']['jwtThinkKey'];
        $jwtArr = explode('.', $jwtTokenArr['token']);
        if (count($jwtArr) != 3) {
            throw new \Exception('jwtToken格式错误');
        }
        $headerStr = base64_decode($jwtArr[0]);
        $header = Tools::isJson($headerStr);
        $payloadStr = base64_decode($jwtArr[1]);
        $payload = Tools::isJson($payloadStr);
        $signature = base64_decode($jwtArr[2]);
        switch ($header['alg']) {
            case 'MD5_THINK_SLAT':
                $signature2 = md5($headerStr . $payloadStr . $thinkKey);
                if ($signature != $signature2) {
                    throw new \Exception('jwtToken签名错误');
                }
                break;
            default:
                throw new \Exception('不支持的解密算法');
        }
        if ($payload['endAt'] < Carbon::now()->timestamp) {
            throw new \Exception('jwtToken已过期');
        }
        $payloadToken = Security::thinkDecrypt($payload['token'], $thinkKey);
        if (substr($payloadToken, -10) != $payload['createdAt']) {
            throw new \Exception('token异常');
        }
        $shopUserToken = substr($payloadToken, 0, 32);
        $shopUser = static::findByToken($shopUserToken);
        if (!$shopUser) {
            throw new \Exception('从token未找到用户');
        }
        if (isset($jwtTokenArr['encryptedMpUserKey'])) {
            $encryptedMpUserKey = md5($shopUser->key.$payload['endAt']);
            if ($encryptedMpUserKey != $jwtTokenArr['encryptedMpUserKey']) {
                throw new \Exception('用户加密key错误');
            }
        }
        $jwt = [
            'jwtToken' => $jwtTokenArr['token'],
            'header' => $header,
            'payload' => $payload,
            'signature' => $signature,
        ];
        return [
            'jwt' => $jwt,
            'encryptedMpUserKey' => md5($shopUser->key.$payload['endAt']),
            'shopUser' => $shopUser,
            'thinkKey' => $thinkKey,
        ];
    }
}
